﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;
[ExecuteAlways]
public class QOS : MonoBehaviour
{
    #region private
    private List<KLine> netData;
    private readonly List<GameObject> kLines = new();
    public string lisence = "8cbf050a08e2bffe9b3ad79cf015fdc3";
    private const string url = "https://api.qos.hk/history?key=6763d479c68549f3073ac7528eb1e160";
    #endregion
    #region public
    public float 持仓收益率;
    public int deleteDays;
    public LineType lineType;
    public float winOrlose;
    public bool showV;
    public static float vScale;
    public string comment;
    public bool update;
    public bool showK;
    public bool showLine;
    private int column;
    public LineRenderer line1;
    public LineRenderer line5;
    public LineRenderer line10;
    public LineRenderer line20;
    public LineRenderer line30;
    public LineRenderer line44;
    public LineRenderer line60;
    public LineRenderer resultLine;
    public StockName stockName;
    public List<QOS> qos;

    private float vLowInDays;
    public float vHighInDays;

    private float pLowInDays;
    private float pHighInDays;
    #endregion

    public float GetScaleValue(float value)
    {
        if (value == 0)
            return 1000;
        switch (lineType)
        {
            case LineType.min30: value *= 9; break;
            case LineType.hour1: value *= 5; break;
        }
        return value;
    }

    public void ClearData()
    {
        //showV = true;
        vLowInDays = 0;
        vHighInDays = 0;
        column = (int)GameObject.FindObjectOfType<Light>().transform.position.x;
        column = (int)GetScaleValue(column);
        vScale = 240f / GameObject.FindObjectOfType<Light>().transform.position.y;
        result = 0;
        kLines.Clear();
        for (var i = transform.childCount - 1; i >= 6; i--)
            GameObject.DestroyImmediate(transform.GetChild(i).gameObject);
        line1 = gameObject.GetComponent<LineRenderer>();

        if (line5 == null) line5 = new GameObject("line5").AddComponent<LineRenderer>();
        if (line10 == null) line10 = new GameObject("line10").AddComponent<LineRenderer>();
        if (line20 == null) line20 = new GameObject("line20").AddComponent<LineRenderer>();
        if (line30 == null) line30 = new GameObject("line30").AddComponent<LineRenderer>();
        if (line44 == null) line44 = new GameObject("line44").AddComponent<LineRenderer>();
        if (line60 == null) line60 = new GameObject("line60").AddComponent<LineRenderer>();

        
        line5.transform.SetParent(transform);
        line10.transform.SetParent(transform);
        line20.transform.SetParent(transform);
        line30.transform.SetParent(transform);
        line44.transform.SetParent(transform);
        line60.transform.SetParent(transform);
    }
    public float height;
    private Action<StockName, float, GameObject> callback;

    public void OnEnabled(Action<StockName, float,GameObject> callback = null)
    {
        this.callback = callback;
        gameObject.SetActive(true);
        OnEnable();
    }
    public bool showBen;
    public GameObject line, benjin;
    public async Task OnEnable()
    {
        if (line == null)
            line = GameObject.Find("持有线");
        if (line)
        {
            line.SetActive(true);
            line.transform.localPosition = new Vector3(1800, height * 20, 0);
        }
        if (benjin == null)
            benjin = GameObject.Find("持有本");
        if (benjin)
        {
            benjin.SetActive(true);
            //
            var delta = (1 + height / 100) / (1 + 持仓收益率 / 100) - 1;
            if (showBen)
                delta = (1 + height / 100) / (1 + winOrlose / 100) / (1 + 持仓收益率 / 100) - 1;
            //Debug.Log("成本线" + delta);
            benjin.transform.localPosition = new Vector3(1800, delta * 100 * 20, 0);

        }
        if (!update) return;
        ClearData();
        //line5 = GetComponent<LineRenderer>();
        StartCoroutine(SendHttpRequest());
        //await SendAsync();
        //Debug.Log(mairuiData);
        try
        {
            var text = JsonUtility.FromJson<StockData>($"{{\"k\":{mairuiData}}}");
            Test(text.k, stockName.ToString());
        }
        catch(Exception e)
        {
            //Debug.LogError(e);
        }
        update = false;
        gameObject.SetActive(true);
    }
    private void Update()
    {
    }
    float start = 0;
    float ben = 0;
    float min, max;
    double minM, maxM;
    float 缺口下沿, 缺口上沿;
    private void Test(List<KLine> data, string stockName)
    {
        for (var i = 1; i <= deleteDays; i++)
            data.RemoveAt(data.Count - 1);
        //data.RemoveAt(data.Count() - 1);
        //data.RemoveAt(data.Count() - 1);
        缺口下沿 = 0;
        缺口上沿 = 0;
        ClearData();
        netData = data;
        min = 100000;
        minM = 0;
        maxM = 0;
        max = 0;
        start = 0;
        KLine lastK = null;
        origin = 0;
        stock = 100;
        if (line1) line1.positionCount = 0;
        if (line5) line5.positionCount = 0;
        if (line10) line10.positionCount = 0;
        if (line20) line20.positionCount = 0;
        if (line30) line30.positionCount = 0;
        if (line44) line44.positionCount = 0;
        if (line60) line60.positionCount = 0;
        if (resultLine) resultLine.positionCount = 0;
        KLine _today = netData.Last();
        netData[netData.Count - 1].v = (netData[netData.Count - 1].Volume * (long)vScale).ToString();
        var temp = _today.Close;
        var qs = 0;
        for (var i = netData.Count - 2; i >= 0; i--)
        {
            if (netData[i].Close >= temp)
            {
                if (qs < 3)
                {
                    vLowInDays++;
                    qs = 0;
                }
                else
                {
                    break;
                }
                temp = netData[i].Close;
            }
            else if (showV)
            {
                qs++;
                vLowInDays++;
                if (qs > 2)
                {
                    vLowInDays -= 3;
                    break;
                }
            }
        }
        qs = 0;
        temp = _today.Close;
        for (var i = netData.Count - 2; i >= 0; i--)
        {
            if (netData[i].Close <= temp)
            {
                if (qs < 3)
                {
                    vHighInDays++;
                    qs = 0;
                }
                else
                {
                    break;
                }
                temp = netData[i].Close;
            }
            else if (showV)
            {
                vHighInDays++;
                qs++;
                if (qs > 2)
                {
                    vHighInDays -= 3;
                    break;
                }
            }
        }
        foreach (var kline in netData)
        {
            if (start == 0)
            {
                start = kline.Open;//(kline.Close + kline.Open) / 2;
                ben = kline.Open;
            }
            if (kline.High > max)
                max = kline.High;
            if (kline.Low < min)
                min = kline.Low;
            if (kline.Volume > maxM)
                maxM = kline.Volume;
            if (kline.Volume < minM || minM == 0)
            {
                if (kline.Volume < 0) 
                    kline.v = "0";
                minM = kline.Volume;
                //Debug.Log("low v is " + minM + ConvertTimestamp(kline.ts + 60 * 60 * 24).Month + "月" + ConvertTimestamp(kline.ts + 60 * 60 * 24).Day);
            }
            if (缺口上沿 != 0 && 缺口下沿 == 0)
                缺口下沿 = Mathf.Max(kline.Close, kline.Open);
            if (缺口上沿 == 0)
                缺口上沿 = Mathf.Min(kline.Close, kline.Open);
        }
        transform.localPosition = new Vector3(0, 0, 0);
        transform.localScale = new Vector3(1, 1, 1);
        float delta = 0;
        foreach (var kline in netData)
        {
            if (lastK != null)
            {
                stock = stock * kline.Close / lastK.Close;
            }
            else
            {
                stock = kline.Close / kline.Open;
            }
            result = (stock - 1) * 100;
            //Strategy1(stock);
            lastK = kline;
        }
        lastK = null;
        var childCount = 0;
        foreach (var kline in netData)
        {
            if (showK)
            {
                var go = GameObject.CreatePrimitive(PrimitiveType.Cube);
                kLines.Add(go);
                var width = Mathf.Abs(kline.Close - kline.Open);
                if (width < 0.0001f) width = 0.0001f;
                go.transform.SetParent(transform);
                
                go.transform.localScale = new Vector3(10, width, 10);
                var date = ConvertTimestamp(kline.ts + 60 * 60 * 24);


                go.transform.localPosition = new Vector3(childCount * 10, (kline.Close + kline.Open) / 2 - ben, 100);
                var line = GameObject.CreatePrimitive(PrimitiveType.Cube);
                line.transform.localScale = new Vector3(1, Mathf.Abs(kline.High - kline.Low), 1);
                line.transform.localPosition = new Vector3(childCount * 10, (kline.High + kline.Low) / 2 - ben, 100);
                line.transform.SetParent(go.transform, true);
                if (kline.Close > kline.Open)
                {
                    if (Application.isPlaying)
                    {
                        go.GetComponent<MeshRenderer>().material.SetColor("_BaseColor", Color.red);
                        line.GetComponent<MeshRenderer>().material.SetColor("_BaseColor", Color.red);
                    }
                    else
                    {
                        go.GetComponent<MeshRenderer>().sharedMaterial.SetColor("_BaseColor", Color.gray);
                        line.GetComponent<MeshRenderer>().sharedMaterial.SetColor("_BaseColor", Color.gray);
                    }
                }
                else
                {
                    if (Application.isPlaying)
                    {
                        go.GetComponent<MeshRenderer>().material.SetColor("_BaseColor", Color.green);
                        line.GetComponent<MeshRenderer>().material.SetColor("_BaseColor", Color.green);
                    }
                    else
                    {
                        go.GetComponent<MeshRenderer>().sharedMaterial.SetColor("_BaseColor", Color.black);
                        line.GetComponent<MeshRenderer>().sharedMaterial.SetColor("_BaseColor", Color.black);
                    }
                }
                if (lastK != null)
                {
                    delta = ((kline.Close - lastK.Close) / lastK.Close * 100);
                    winOrlose = (kline.Close - lastK.Close) / lastK.Close * 100;
                    go.name = date.Month + "月" + date.Day + "日" + (delta > 0 ? "+" : "") + delta.ToString("F2") + "%";
                }
                else
                {
                    delta = ((kline.Close - kline.Open) / kline.Open * 100);
                    winOrlose = (kline.Close - kline.Close) / kline.Open * 100;
                    go.name = date.Month + "月" + date.Day + "日" + (delta > 0 ? "+" : "") + delta.ToString("F2") + "%";
                }
                //go.name = kline.t.Substring(5, 5).Replace("-", "月") + (delta > 0 ? "+" : "") + delta.ToString("F2") + "%";
            }

            if (lastK != null)
            {
                delta = ((kline.Close - lastK.Close) / lastK.Close * 100);
                winOrlose = (kline.Close - lastK.Close) / lastK.Close * 100;
            }
            else
            {
                delta = ((kline.Close - kline.Open) / kline.Open * 100);
            }
            if (showLine)
            {
                //SetMyLine(line1, childCount);
                SetVolumnLine(line1, childCount);
                //SetELine(line1, 1, childCount);
                SetELine(line5, 5, childCount);
                SetELine(line10, 10, childCount);
                SetELine(line20, 20, childCount);
                SetELine(line30, 30, childCount);
                SetELine(line44, 40, childCount);
                SetELine(line60, 60, childCount);
            }
            childCount++;
            lastK = kline;
        }
        height = result;
        transform.localPosition = new Vector3(0, 0, 0);
        var y = (netData.Last().Close - ben) / netData.Last().Close;
        y = 2000 / y;
        y /= ben;
        y /= (result + 100);
        y *= result;

        y = GetScaleValue(y);
        height = GetScaleValue(height);

        transform.localScale = new Vector3(1, Mathf.Abs(y), 1);
        var today = transform.GetChild(transform.childCount - 1);

        //累计涨幅
        if (Math.Abs(result) < 10 && Math.Abs(result) >= 1)
        {
            if (result < 0)
                name = "-0" + Math.Abs(result).ToString("F2") + "%";
            else
                name = "0" + result.ToString("F2") + "%";
        }
        else
            name = result.ToString("F2") + "%";
        if (showV)
        {
            if (vLowInDays > 0)
                name = "趋势向下" + vLowInDays;
            if (vHighInDays > vLowInDays)
                name = "趋势向上" + vHighInDays;
        }
        if (delta > 0)
            name += "收+" + delta.ToString("F2") + "%" + "价";
        else
            name += "收" + delta.ToString("F2") + "%" + "价";
        var per = (lastK.Close - min) / (max - min) * 100;
        if (Math.Abs(per) < 10 && Math.Abs(per) >= 1)
        {
            if (per < 0)
                name += "-0" + Math.Abs(per).ToString("F1") + "%";
            else
                name += "0" + per.ToString("F1") + "%";
        }
        else
            name += per.ToString("F1") + "%";
        if (!showV)
        {
            name += "量";
            var volumn = ((netData.Last().Volume - minM) / (maxM - minM) * 100);
            //volumn = (vHighInDays + 1) / column * 100;
            if (volumn < 10 && volumn >= 1)
                name += "0" + volumn.ToString("F2") + "%";
            else
                name += volumn.ToString("F2") + "%";
        }
        name += stockName;
        if (!name.Contains(comment)) name += comment + 持仓收益率;
        //name = "补缺" + (netData.Last().Close - 缺口下沿) / (缺口上沿 - 缺口下沿) * 100 + "%" + name;


        var close = GetScaleValue(20 * result);
        if (showLine)
        {
            if (column >= 5 && close >= line5.GetPosition(line5.positionCount - 1).y)
            {
                SetParent(transform, "下踩5");
            }
            else if (column >= 10 && close >= line10.GetPosition(line10.positionCount - 1).y)
            {
                if (Mathf.Abs(close - line5.GetPosition(line5.positionCount - 1).y) <
                    Mathf.Abs(close - line10.GetPosition(line10.positionCount - 1).y))
                    SetParent(transform, "下破5");
                else
                    SetParent(transform, "下踩10");
            }
            else if (column >= 20 && close >= line20.GetPosition(line20.positionCount - 1).y)
            {
                if (Mathf.Abs(close - line10.GetPosition(line10.positionCount - 1).y) <
                    Mathf.Abs(close - line20.GetPosition(line20.positionCount - 1).y))
                    SetParent(transform, "下破10");
                else
                    SetParent(transform, "下踩20");
            }
            else if (column >= 30 && close >= line30.GetPosition(line30.positionCount - 1).y)
            {
                if (Mathf.Abs(close - line20.GetPosition(line20.positionCount - 1).y) <
                    Mathf.Abs(close - line30.GetPosition(line30.positionCount - 1).y))
                    SetParent(transform, "下破20");
                else
                    SetParent(transform, "下踩30");
            }
            else if (column >= 44 && close >= line44.GetPosition(line44.positionCount - 1).y)
            {
                if (Mathf.Abs(close - line20.GetPosition(line30.positionCount - 1).y) <
                    Mathf.Abs(close - line30.GetPosition(line44.positionCount - 1).y))
                    SetParent(transform, "下破30");
                else
                    SetParent(transform, "下踩44");
            }
            else if (column >= 60 && close >= line60.GetPosition(line60.positionCount - 1).y)
            {
                if (Mathf.Abs(close - line30.GetPosition(line44.positionCount - 1).y) <
                    Mathf.Abs(close - line60.GetPosition(line60.positionCount - 1).y))
                    SetParent(transform, "下破44");
                else
                    SetParent(transform, "下踩60");
            }
            else
                SetParent(transform, "下破60");
        }

        transform.SetAsFirstSibling();
        for (var i = 0; i < transform.parent.childCount; i++)
        {
            var child = transform.parent.GetChild(i);
            var qos = child.GetComponent<QOS>();
            if (winOrlose < qos.winOrlose)
            {
                transform.SetSiblingIndex(i);
            }
        }
        callback?.Invoke(this.stockName, delta, gameObject);
    }

    private void SetParent(Transform transform,string parentName)
    {
        var parent = GameObject.Find(parentName).transform;
        if (parent == null)
            parent = new GameObject(parentName).transform;
        if (transform.parent != parent)
        {
            Debug.Log(transform.name.Split("丶")[1] + "从" + transform.parent.name + "变" + parent.name, transform.gameObject);
            transform.SetParent(parent);
        }
    }

    private void SetVolumnLine(LineRenderer line, int now)
    {
        line.startWidth = 1f;
        line.endWidth = 1f;
        var v = netData[now].Volume;
        line.positionCount++;
        line.SetPosition(line.positionCount - 1, new Vector3((now) * 10, (float)((v - minM) / (maxM - minM) * 200f), 100));
    }

    private double value;
    private double amount;
    private void SetMyLine(LineRenderer line, int now)
    {
        line.positionCount++;
        if (now == 1)
        {
            value = 0;
            amount = 0;
        }
        //else
        {
            value += netData[now - 1].Close * netData[now - 1].Volume * GetX(now);
            amount += netData[now - 1].Volume * GetX(now);
        }
        line.SetPosition(line.positionCount - 1, new Vector3(now * 10, ((float)(value / amount) - min) / (max - min) * 1000, 100));
    }

    /// <summary>
    /// 加权
    /// </summary>
    private float GetX(int now)
    {
        return MathF.Pow(now, MathF.E);
    }

    private void SetELine(LineRenderer line, int days, int now)
    {

        line.startWidth = 1f;
        line.endWidth = 1f;
        if (line && now + 1 >= days)
        {
            float e = 0;
            for (var i = now; i > now - days; i--)
            {
                var date = ConvertTimestamp(netData[i].ts + 60 * 60 * 24);
                e += netData[i].Close;
            }
            e /= days;
            if (days == 1)
                e = netData[now].Close;
            line.positionCount++;
            line.SetPosition(line.positionCount - 1, new Vector3((now) * 10, GetScaleValue(1) * (e - ben) * 2000 / (netData.Last().Close - ben) * netData.Last().Close / ben / (result + 100) * result, 100));
        }
    }

    // 返回当前时间的毫秒级时间戳
    public static long GetCurrentTimestampMilliseconds()
    {
        DateTime epoch = new(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
        TimeSpan timeSpan = DateTime.UtcNow - epoch;
        return (long)timeSpan.TotalSeconds;
    }

    public static long GetYesterday()
    {
        DateTime epoch = new(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
        TimeSpan timeSpan = DateTime.UtcNow - epoch;
        return (long)timeSpan.TotalSeconds - (19 + 24) * 60 * 60;
    }

    // 时间戳转DateTime（UTC）
    private DateTime ConvertTimestamp(long timestamp)
    {
        DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
        return epoch.AddSeconds(timestamp);
    }
    private string mairuiData;
    private async Task SendAsync()
    {
        var code = stockName.ToString()[..9].Split("_");
        var xxx = code[1] + "." + code[0];
        var url = "https://api.mairuiapi.com/hsindex/history/" + xxx + "/d/" + lisence + "?lt=" + column;
        using (HttpClient client = new HttpClient())
        {
            HttpResponseMessage response = await client.GetAsync(url);
            mairuiData = await response.Content.ReadAsStringAsync();
        }
    }

    private IEnumerator SendHttpRequest()
    {
        //以下是qos.hk的用法
        string jsonParams = "{\"kline_reqs\": [";
        foreach (var code in qos)
        {
            jsonParams += "{" +
            "\"c\": \"" + code.stockName.ToString()[..9].Replace("_", ":") + "\"," +
            "\"co\": " + column + "," +
            "\"kt\": " + (int)lineType + "}";
            if (code != qos.Last())
                jsonParams += ",";
        }
        jsonParams += "]}";

        byte[] postData = new UTF8Encoding().GetBytes(jsonParams);


        UnityWebRequest request = new UnityWebRequest(url, "POST")
        {
            uploadHandler = new UploadHandlerRaw(postData),
            downloadHandler = new DownloadHandlerBuffer()
        };

        request.SetRequestHeader("Content-Type", "application/json");
        yield return request.SendWebRequest();

        if (request.result == UnityWebRequest.Result.ConnectionError || request.result == UnityWebRequest.Result.ProtocolError)
        {
            Debug.LogError(request.error);
        }
        else
        {
            // 请求成功，处理响应
            //Debug.Log("Response: " + request.downloadHandler.text, gameObject);
            StockResponse response = JsonUtility.FromJson<StockResponse>(request.downloadHandler.text);
            foreach (var data in response.data)
            {
                foreach (var item in qos)
                {
                    if (item.stockName.ToString()[..9].Replace("_", ":") == data.c)
                    {
                        //if (item.qos.Count == 0)
                        item.callback = this.callback;
                        item.update = false;
                        item.Test(data.k, item.stockName.ToString());
                        item.gameObject.SetActive(true);
                    }
                }
            }
        }
    }

    private float origin;
    private float stock;
    private void Buy(float value)
    {
        if (origin >= value)
        {
            stock += value;
            origin -= value;
        }
    }

    private void Sell(float value)
    {
        if (stock >= value)
        {
            stock -= value;
            origin += value;
        }
    }

    private string Result()
    {
        return (origin + stock - 100).ToString("F2") + "%";
    }

    private void Strategy1(float value)
    {
        Buy(value);
    }

    private void Strategy2(KLine line, KLine last)
    {
        if (last != null)
        {
            var delta = line.Close / last.Close;
            if (delta > 1)
            {
                if (last.Volume > line.Volume)
                {
                    if (line.Open < 3000)
                        Buy((delta - 1) * buyDelta);
                    else if (line.Open > 3400)
                        Sell((delta - 1) * sellDelta);
                }
            }
            else
            {
                if (last.Volume > line.Volume)
                {
                    if (line.Open > 3400)
                        Sell((delta - 1) * sellDelta);
                    else if (line.Open < 3200)
                        Buy((1 - delta) * buyDelta);
                }
            }
        }
        result = origin + stock;
        var percent = stock / result;
        if (resultLine)
        {
            resultLine.positionCount++;
            resultLine.SetPosition(resultLine.positionCount - 1, new Vector3((resultLine.positionCount - 1) * 10, result * 10, 100 * percent));
        }
    }
    public float result;
    public float buyDelta = 100;
    public float sellDelta = 100;
}
